#include "MapLoader.h"
#include <fstream>
#include "ResourceManager.h"

using namespace std;

MapInfo MapLoader::loadMapInfo(const std::string& dirName)
{
	MapData data;
	ifstream file(("../maps/" + dirName + "/data.bin").c_str(), ios::in | ios::binary);
	if (!file.is_open()) {
		abort();
	}
	file.read(reinterpret_cast<char*>(&data), sizeof(MapData));

	sf::Image* mapImage = ResourceManager::getInstance()->loadImage("../maps/" + dirName + "/images.bmp");
	mapImage->CreateMaskFromColor(mapImage->GetPixel(data.transparentPosX, data.transparentPosY));
	vector<sf::IntRect> rects = getRects(data);

	MapInfo info;
	char* buff = new char[data.mapWidth];
	//Sprites
	for (int row = 0; row < data.mapHeight; ++row) {
		file.read(buff, data.mapWidth);
		for (int col = 0; col < data.mapWidth; ++col) {
			short value = buff[col];
			if (value) {
				sf::Sprite block(*mapImage, mapToWorld(row, col, data));
				block.SetSubRect(rects[value - 1]);
				info.sprites.push_back(block);
			}
		}
	}
	//Bounds
	for (int row = 0; row < data.mapHeight; ++row) {
		file.read(buff, data.mapWidth);
		short lastBound = 0;
		for (int col = 0; col < data.mapWidth; ++col) {
			short value = buff[col];
			parseShape(&info.bounds, row, col, value, &lastBound);
		}
	}
	delete[] buff;
	file.close();
	return info;
}

vector<sf::IntRect> MapLoader::getRects(const MapData& mapData)
{
	vector<sf::IntRect> rects;
	for (int y = 0; y < mapData.verticalTileCount; ++y) {
		for (int x = 0; x < mapData.horizontalTileCount; ++x) {
			rects.push_back(sf::IntRect(
				x * mapData.tileWidth,
				y * mapData.tileHeight,
				(x + 1) * mapData.tileWidth,
				(y + 1) * mapData.tileHeight
			));
		}
	}
	return rects;
}

sf::Vector2f MapLoader::mapToWorld(const int& row, const int& col, const MapData& mapData)
{
	float worldX = (float)((col) * mapData.tileWidth);
	float worldY = (float)((row) * mapData.tileHeight);
	return sf::Vector2f(worldX, worldY);
}

//TODO retirar ratios hard coded
void MapLoader::parseShape(vector<b2EdgeChainDef>* shapes, const int& row, const int& col, const short& boundValue, short* lastBound)
{
	if (boundValue != 0) {
		float xoffset = col + 0.5f;
		float yoffset = -row - 0.5f;
		int vertexCount;
		b2Vec2* vertices;
		if (boundValue & BoundMask::DIAGONAL_FLAG) {
			vertexCount = 2;
			vertices = new b2Vec2[vertexCount];
			if (boundValue & BoundMask::DIAGONAL_DIR) {
				vertices[0].Set(0.5f + xoffset, 0.0f + yoffset);
				vertices[1].Set(-0.5f + xoffset, 0.5f + yoffset);
			} else {
				vertices[0].Set(0.5f + xoffset, 0.5f + yoffset);
				vertices[1].Set(-0.5f + xoffset, 0.0f + yoffset);
			}
		} else {
			//TODO adicionar novos tipos de bounds
			if (boundValue & BoundMask::UPPER) {
				if (*lastBound & BoundMask::UPPER) {
					shapes->back().vertices[0].x += 1.0f;
					return;
				}
				vertexCount = 2;
				vertices = new b2Vec2[vertexCount];
				vertices[0].Set(0.5f + xoffset, 0.5f + yoffset);
				vertices[1].Set(-0.5f + xoffset, 0.5f + yoffset);
			}
		}
		b2EdgeChainDef groundShapeDef;
		groundShapeDef.friction = 0.5f;
		groundShapeDef.vertexCount = vertexCount;
		groundShapeDef.vertices = vertices;
		groundShapeDef.isALoop = vertexCount > 2;
		shapes->push_back(groundShapeDef);
	}
	*lastBound = boundValue;
}
